package com.tfswx.jcyw.dmzc.codeInspection;

import com.intellij.codeInspection.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;


public final class ApiLevelInspection extends AbstractBaseJavaLocalInspectionTool {
    private static final Set<String> MAPPING_NAME_SET;

    static {
        MAPPING_NAME_SET = new HashSet<>(Arrays.asList(FixAnnotationEnum.PatchMapping.getQualifiedName(), FixAnnotationEnum.PutMapping.getQualifiedName(), FixAnnotationEnum.PostMapping.getQualifiedName(), FixAnnotationEnum.GetMapping.getQualifiedName(), FixAnnotationEnum.RequestMapping.getQualifiedName(), FixAnnotationEnum.DeleteMapping.getQualifiedName()));
    }

    @Override
    public @NotNull PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) {
        return new JavaElementVisitor() {
            @Override
            public void visitMethod(PsiMethod psiMethod) {
                if (JavaPsiFacade.getInstance(psiMethod.getProject()).findClass(FixAnnotationEnum.LevenApi.getQualifiedName(), GlobalSearchScope.allScope(psiMethod.getProject())) != null) {
                    PsiAnnotation[] methodAnnotations = psiMethod.getAnnotations();
                    PsiAnnotation mappingAnnotation = null;
                    PsiAnnotation levenApiAnnotation = null;

                    for (PsiAnnotation methodAnnotation : methodAnnotations) {
                        if (ApiLevelInspection.MAPPING_NAME_SET.contains(methodAnnotation.getQualifiedName())) {
                            mappingAnnotation = methodAnnotation;
                        } else if (FixAnnotationEnum.LevenApi.getQualifiedName().equals(methodAnnotation.getQualifiedName())) {
                            levenApiAnnotation = methodAnnotation;
                        }
                    }

                    if (mappingAnnotation != null && levenApiAnnotation == null && psiMethod.getNameIdentifier() != null) {
                        PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier();
                        holder.registerProblem(nameIdentifier, "添加 @LevenApi注解", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new LevenApiQuickFix(psiMethod));
                    }

                }
            }
        };
    }

    public enum FixAnnotationEnum {
        GetMapping("GetMapping", "org.springframework.web.bind.annotation.GetMapping"),
        PostMapping("PostMapping", "org.springframework.web.bind.annotation.PostMapping"),
        PutMapping("PutMapping", "org.springframework.web.bind.annotation.PutMapping"),
        PatchMapping("PatchMapping", "org.springframework.web.bind.annotation.PatchMapping"),
        RequestMapping("RequestMapping", "org.springframework.web.bind.annotation.RequestMapping"),
        DeleteMapping("DeleteMapping", "org.springframework.web.bind.annotation.DeleteMapping"),
        LevenApi("LevenApi", "com.test.ann.LevenApi"),
        ;


        @Getter
        private final String shortName;

        @Getter
        private final String qualifiedName;


        FixAnnotationEnum(String shortName, String qualifiedName) {
            this.shortName = shortName;
            this.qualifiedName = qualifiedName;
        }
    }

    public static final class LevenApiQuickFix implements LocalQuickFix {
        private final SmartPsiElementPointer<PsiMethod> smartPointerManager;


        public LevenApiQuickFix(PsiMethod psiMethod) {
            super();
            smartPointerManager = SmartPointerManager.createPointer(psiMethod);
        }

        @Override
        public @NotNull String getFamilyName() {
            return "创建 @LevenApi 注解";
        }

        @Override
        public void applyFix(final @NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IncorrectOperationException {
            final PsiClass levenApiClass = JavaPsiFacade.getInstance(project).findClass(ApiLevelInspection.FixAnnotationEnum.LevenApi.getQualifiedName(), GlobalSearchScope.projectScope(project));
            if (levenApiClass != null) {
                ApplicationManager.getApplication().invokeLater(() -> WriteCommandAction.runWriteCommandAction(project, () -> {
                    PsiMethod psiMethod = smartPointerManager.getElement();
                    if (psiMethod == null) {
                        return;
                    }
                    PsiClass containingClass = psiMethod.getContainingClass();
                    if (containingClass != null) {
                        PsiFile containingFile = containingClass.getContainingFile();
                        if (containingFile == null) {
                            return;
                        }
                        PsiImportList list = ((PsiJavaFile) containingFile).getImportList();
                        if (list != null) {
                            importLevenApiClass(project, levenApiClass, list);
                            psiMethod.getModifierList().addAnnotation("LevenApi(name = \"" + psiMethod.getName() + "\")");
                        }

                    }
                }));
            }

        }

        private void importLevenApiClass(Project project, PsiClass levenApiClass, PsiImportList importList) {
            boolean notExistLevenApiImportStatement = true;
            PsiImportStatement[] importStatements = importList.getImportStatements();

            PsiImportStatement levenApiImportStatement;
            for (PsiImportStatement psiImportStatement : importStatements) {
                levenApiImportStatement = psiImportStatement;
                String qualifiedName = FixAnnotationEnum.LevenApi.getQualifiedName();
                if (qualifiedName.equals(levenApiImportStatement.getQualifiedName())) {
                    notExistLevenApiImportStatement = false;
                }
            }

            if (notExistLevenApiImportStatement) {
                levenApiImportStatement = JavaPsiFacade.getElementFactory(project).createImportStatement(levenApiClass);
                importList.add(levenApiImportStatement);
            }

        }
    }
}
